#! /usr/bin/ruby
require 'rexml/document'
require 'open-uri'
require 'ostruct'

module Cpg
  
  PAYLOAD_PATH        = "payload"
  PAYLOAD_CUSTOM_PATH = "payload/custom_packages"
  CUSTOM_PACK_PATH    = "custom_packages"
  
  class Plist
    
    def self.correct?(plist_source)
      xmlsource = plist_source =~ /^http.+/ ? open(plist_source,"User-Agent" => "AppTapp/3.0.1") : File.open(plist_source)
      customized_plist = REXML::Document.new(xmlsource)
      result = customized_plist.elements["plist/dict/array"] ? true : false
    end
  
    def initialize(plist_source)
      xmlsource = plist_source =~ /^http.+/ ? open(plist_source,"User-Agent" => "AppTapp/3.0.1") : File.open(plist_source)
      @source = REXML::Document.new(xmlsource)
    end
    
    def get_source_dict_xml
      key_item_names = %w(category contact description location maintainer name url)
      result_xml = []
      key_item_names.each do |key_name| 
      result_xml << %Q(
        <key>#{key_name}</key>
        <string></string>)
      end
      %Q(
      <dict>#{result_xml}
      </dict>
      )
    end



    def update_packagesources_list(local_plist_path, server_url)
      begin
      
        if File.exist?(local_plist_path)
          localpackages_plist = REXML::Document.new(File.open(local_plist_path))
        else
          return false
        end

        repo = open(server_url,"User-Agent" => "AppTapp/3.0.1")
        xml_source = REXML::Document.new(repo)
        # if xml_source is html formate
        if xml_source.elements["html"]
          puts "addsource #{server_url}....[FAILED]\n"
          return false
        end
        check_point = true
        temp_doc = REXML::Document.new(get_source_dict_xml)
        
        temp_doc.elements.each("dict/key") do |key_item|  
          xml_source.elements.each("plist/dict/dict/key") do |server_info|
            if ( key_item.text == server_info.text ) 
              key_item.next_element.text = server_info.next_element.text
            end
            if (key_item.text == 'location')
              key_item.next_element.text = server_url
            end
          end
          #判斷寫入的source info是否正確被抓回來並且寫入正確 若否則跳出
          if ( ( key_item.text == 'name' )&&( key_item.next_element.text == nil ))
            check_point = false
          end
        end
      
        if check_point #若之前沒有出現異常則寫入 若有異常 則顯示退出 不在剛剛迴圈退出的原因是 name很慢才會寫到....
          localpackages_plist.elements["plist/array"].add(temp_doc)
          File.open(local_plist_path, "w") do |file|
            file << localpackages_plist.to_s
          end  #底下的顯示只是debug用 可以關掉
          #print "add #{server_url}....[DONE]\n"
        else
          print "addsource #{server_url}....[FAILED]\n"
          return false
        end
        return true
      rescue Exception
        puts "addsource #{server_url}....[FAILED]\n"
        return false
      end
      
    end
    
    def parse
      apps = []
      @source.elements.each("plist/dict/array/dict") do |dict|
          app = App.new
          
          dict.elements.each("key") do |key|
            # dict = <key>bundleIdentifier</key>
            case key.text
            when "bundleIdentifier"
              app.bundleIdentifier = key.next_element.text
            when "location"
              app.location = key.next_element.text
            when "name"
              app.name = key.next_element.text
              # 將名字變成CamelCase
              app.name.gsub!(/ (.)/) { $1.upcase }
              app.name.gsub!(/\'/, "")
              app.name.gsub!(/\\/, "")

            when "version"
              app.version = key.next_element.text
            when "scripts"
              app.scripts = parse_script(key.next_element, app.name)
            end
          end
          apps << app
      end
      apps
    end
    
    
    def parse_script(script_element, appname)
      scripts = OpenStruct.new
      script_element.elements.each("key") do |action|
        case action.text
        when "install"
          scripts.install = parse_install(action.next_element, appname)
        # when "preflight"
        end
      end
      scripts
    end
  
    def parse_install(install_element, appname)
      commands = []
      install_element.elements.each("array/string") do |command|
        
          relative_path = "${PL_DIR}/${PL}/" + "#{CUSTOM_PACK_PATH}/#{appname}/"

          case command.text
          when "CopyPath"
            source_path = command.next_element.text.to_s
            dest_path   = command.next_element.next_element.text.to_s
            
            case source_path
            # 可能的路徑
            # /Navizon.app
            # /private/var/root/Library/Customize/Leopard

            when /^\/.+/
              # 絕對路徑: /private/var/root/Library/Customize/Leopard  =>  /private/var/root/Library/Customize/Leopard/*
              option = "-R "
            else
              source_path = relative_path + source_path
              option = "-R "
            end

            # transfer ~ path to /var/root
            if dest_path =~ /^~(\/.+)/
              dest_path = "/var/root" + $1
            end

            # transfer space in path
            source_path = source_path.gsub(/\s/, "\\ ")
            dest_path = dest_path.gsub(/\s/, "\\ ")

            commands << "cp " + option.to_s + source_path + " " + dest_path


          when "Exec"
            commands << command.next_element.text
          when "InstallApp"
            source_path = relative_path + command.next_element.text.to_s
            option = "-R "
            source_path = source_path.gsub(/\s/, "\\ ")
            commands << "cp " + option + source_path + " " + "/Applications"
          when "AddSource"
            commands << "# [AddSource]#{command.next_element.text.to_s}"
            #if update_packagesources_list( "payload/installer/installer/PackageSources.plist" , command.next_element.text )
            #  commands << "# [AddSource]#{command.next_element.text.to_s} Successed"
            #else
            #  commands << "# [AddSource]#{command.next_element.text.to_s} Failed"
            #end
          end
      end
      commands
    end
  
    def update_localpackage_list(local_plist_path, app_names)
      if File.exist?(local_plist_path)
        localpackages_plist = REXML::Document.new(File.open(local_plist_path))
      else
        return false
      end
      
      key_item_names = %w(bundleIdentifier category contact date description location maintainer name scripts size source version)
      #package_list = REXML::Document.new(load_source_xml(xmlpath))
      #install_localpackages_plist = REXML::Document.new(load_source_xml(installer_local_plist))

      @source.elements.each("plist/dict/array/dict") do |dict|

        dict.elements.each("key") do |key|
          if key.text == "bundleIdentifier"
            unless app_names.include?(key.next_element.text)
              next
            end
          end
        end

        temp_doc = REXML::Document.new(get_installer_dict_xml)
        dict.elements.each("key") do |key|    
          if key_item_names.include?(key.text)
            if(key.text == "scripts")
              key_element = find_key_element(temp_doc, key.text)
              key.next_element.elements.each do |e|
                key_element.next_element.add(e)
              end

            else
              key_element = find_key_element(temp_doc, key.text)
              key_element.next_element.text = key.next_element.text

            end
          end

        end

        localpackages_plist.elements["plist/array"].add(temp_doc)

      end

      File.open(local_plist_path, "w") do |file|
        file << localpackages_plist.to_s
      end
      
      return true
      
    end
    
    #製作packagesource.plist的template
    def get_installer_dict_xml
      key_item_names = %w(bundleIdentifier category contact date description location maintainer name scripts size source version)
      result_xml = []
      key_item_names.each do |key_name| 
        if(key_name == "scripts")
          result_xml << %Q(
        <key>scripts</key>
        <dict></dict>)
        else
          result_xml << %Q(
        <key>#{key_name}</key>
        <string></string>)
        end
      end
      %Q(
      <dict>#{result_xml}
      </dict>)
    end

    def find_key_element(source, keyword)
      source.elements.each("dict/key") do |key_element|
        if key_element.text == keyword
          return key_element
        end
      end
      return nil
    end
    
  end
  
end

